<template>
  <div>
    <canvas class="webgl"></canvas>
    <div class="vr">
      <span class="box">
        <i class="icon"></i>
        <b class="text">全景漫游</b>
      </span>
    </div>
    <!-- 场景切换 -->
    <div class="switch">
      <span class="button"
      v-for="(room, index) in rooms"
      :key="index"
      @click="handleSwitchButtonClick(room.key)"
      v-show="room.key !== data.currentRoom"
      >
        <b class="text">{{room.name}}</b>
        <i class="icon"></i>
      </span>
    </div>
    <!-- 交互点 -->
    <div class="point"
    v-for="(point, index) in interactivePoints"
    :key="index"
    :class="[`point-${index}`, `point-${point.key}`]"
    @click="handleReactivePointClick(point)"
    v-show="point.room === data.currentRoom"
    >
      <div class="label" :class="[`label-${index}`, `label-${point.key}`]">
        <label class="label-tips">
          <div class="cover">
            <i class="icon" :style="{background: `url(${point.cover}) no-repeat center`, 'background-size': 'contain'}"></i>
          </div>
          <div class="info">
            <p class="p1">{{point.value}}</p>
            <p class="p2">{{point.description}}</p>
          </div>
        </label>
      </div>
    </div>
  </div>
</template>

<script setup>
import * as THREE from 'three'
// 控制器
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { computed, onMounted, reactive } from 'vue'
import { TWEEN } from 'three/examples/jsm/libs/tween.module.min.js';
import { rooms } from './data';
import { toast, sleep } from '../../utils/index'
let scene, camera, renderer, controls;
const data = reactive({
  currentRoom: 'living-room'
})

// 点击切换场景
const handleSwitchButtonClick = async (key) => {
  const room = rooms.filter(item => item.key === key)[0];
  if (camera) {
    // const x = room.position.x;
    // const y = room.position.y;
    // const z = room.position.z;
    controls && controls.update();
  }
  await sleep(1600);
  data.currentRoom = room.key;
}
// 点击交互点
const handleReactivePointClick = (point) => {
  toast(`您点击了${point.value}`);
}

// 获取交互点的信息
const interactivePoints = computed(() => {
  const res = [];
  rooms.forEach(room => {
    if (room.interactivePoints && room.interactivePoints.length>0) {
      room.interactivePoints.forEach(point => {
        point = {
          room: room.key,
          ...point
        };
        res.push(point)
      })
    }
  })
  return res
})

onMounted(() => {
  // 渲染器
  function initThree() {
    // 场景
    scene = new THREE.Scene();
    // 相机
    camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000)
    camera.position.set(0, 0, 0.01)
    scene.add(camera)

    // 渲染器
    const canvas = document.querySelector('canvas.webgl');
    renderer = new THREE.WebGLRenderer({canvas})
    renderer.setSize(window.innerWidth, window.innerHeight)
    // document.getElementById('container').appendChild(renderer.domElement)

    // 添加坐标辅助器
    // const axesHelper = new THREE.AxesHelper(5)
    // scene.add(axesHelper);

    // 镜头控制器
    controls = new OrbitControls(camera, renderer.domElement)
    controls.target.set(0, 0, 0);
    controls.enablePan = false;
    // 缩放限制
    controls.maxDistance = 0.1;
    // 垂直旋转限制
    controls.minPolarAngle = Math.PI / 2;
    controls.maxPolarAngle = Math.PI / 2;
    controls.enableDamping = true // 镜头启动惯性
    let materials = []; // 材质
    let leftImg = require('../../assets/threeJs/house/left.jpeg'),
      rightImg = require('../../assets/threeJs/house/right.jpeg'),
      topImg = require('../../assets/threeJs/house/top.jpeg'),
      bottomImg = require('../../assets/threeJs/house/bottom.jpeg'),
      frontImg = require('../../assets/threeJs/house/front.jpeg'),
      backImg = require('../../assets/threeJs/house/back.jpeg');

    //根据上下左右前后顺序构建六个面材质集, 纹理加载器
    let texture_left = new THREE.TextureLoader().load(leftImg)
    materials.push(new THREE.MeshBasicMaterial({map: texture_left}))
    let texture_right = new THREE.TextureLoader().load(rightImg)
    materials.push(new THREE.MeshBasicMaterial({map: texture_right}))
    let texture_top = new THREE.TextureLoader().load(topImg)
    materials.push(new THREE.MeshBasicMaterial({map: texture_top}))
    let texture_bottom = new THREE.TextureLoader().load(bottomImg)
    materials.push(new THREE.MeshBasicMaterial({map: texture_bottom}))
    let texture_front = new THREE.TextureLoader().load(frontImg)
    materials.push(new THREE.MeshBasicMaterial({map: texture_front}))
    let texture_back = new THREE.TextureLoader().load(backImg)
    materials.push(new THREE.MeshBasicMaterial({map: texture_back}))
    let cube = new THREE.Mesh(new THREE.BoxGeometry(1,1,1), materials);
    scene.add(cube);
    cube.geometry.scale(1, 1, -1)

    // 室内悬浮标记物
    const raycaster = new THREE.Raycaster(); // 光线投射
    const _points = interactivePoints.value.map((item, index) => ({
      ...item,
      element: document.querySelector(`.point-${index}`)
    }))

    // 动画
    const tick = () => {
      if (renderer) {
        for (const point of _points) {
          const screenPosition = point.position.clone();
          const pos = screenPosition.project(camera);
          raycaster.setFromCamera(screenPosition, camera);
          const intersects = raycaster.intersectObjects(scene.children, true);
          if (intersects.length === 0) {
            // 未找到相交点，显示
            point.element.classList.add('visible');
          } else {
            // 找到相交点
            // 获取相交点的距离和点的距离
            const intersectionDistance = intersects[0].distance;
            const pointDistance = point.position.distanceTo(camera.position);
            // 相交点距离比点距离近，隐藏；相交点距离比点距离远，显示
            intersectionDistance < pointDistance
            ? point.element.classList.remove('visible')
            : point.element.classList.add('visible');
          }
          pos.z > 1
          ? point.element.classList.remove('visible')
          : point.element.classList.add('visible');
          const translateX = screenPosition.x * window.innerWidth * 0.5;
          const translateY = -screenPosition.y * window.innerHeight * 0.5;
          point.element.style.transform = `translateX(${translateX}px) translateY(${translateY}px)`;
        }
      }
      controls && controls.update();
      TWEEN && TWEEN.update();
      // 更新渲染器
      renderer.render(scene, camera);
      // 页面重绘时调用自身
      window.requestAnimationFrame(tick);
    }

    tick();


    //页面缩放事件监听
    window.addEventListener('resize', () => {
      // 重置渲染器输出画布canvas尺寸
      renderer.setSize(window.innerWidth, window.innerHeight);
      // 全屏模式：设置观察范围长宽比
      camera.aspect = window.innerWidth / window.innerHeight;
      // 如果相机的一些属性发生了变化，需要执行updateProjectionMatrix ()方法更新相机的投影矩阵
      camera.updateProjectionMatrix();

    })
    animation()
  }
  function animation() {
    controls.update()
    renderer.render(scene, camera)
    requestAnimationFrame(animation)
  }
  initThree()
})
</script>

<style lang="scss" scoped>
.webgl{
  position: fixed;
  top: 0;
  left: 0;
  outline: none;
}
.vr{
  position: fixed;
  top: 0;
  left: 0;
  z-index: 1;
  animation: slideInLeft 1s .15s;
  animation-fill-mode: both;
  .box{
    display: inline-block;
    background: rgba(0,0,0,0.3);
    backdrop-filter: blur(8px);
    display: flex;
    align-items: center;
    justify-content: space-around;
    overflow: hidden;
    padding: 4px 2px;
    border-radius: 0 0 16px 0;
    border: 1px groove rgba(255, 255, 255, .3);
    border-top: none;
    border-left: none;
    box-shadow: 0 1px 4px rgba(0, 0, 0, .1);
  }
  .icon{
    display: inline-block;
    height: 64px;
    width: 64px;
    background: url('../../assets/threeJs/home/vr.png') no-repeat center;
    background-size: contain;
    margin-right: 12px;
  }
  .text{
    font-size: 24px;
    color: #fff;
    display: inline-block;
    font-weight: 500;
  }
}
.switch{
  position: fixed;
  right: 24px;
  top: 40%;
  z-index: 1;
  animation: slideInRight 1s .3s;
  animation-fill-mode: both;
  .button+.button{
    margin-top: 18px;
  }
  .button{
    display: block;
    background: rgba(27, 25, 24, .5);
    border-radius: 12px;
    display: flex;
    align-content: center;
    padding: 12px 8px 12px 24px;
    cursor: pointer;
    transition: all 0.25s ease-in-out;
  }
  .text{
    color: rgba(255, 255, 255, 1);
    font-size: 24px;
    font-weight: 600;
  }
  .icon{
    display: inline-block;
    width: 30px;
    height: 30px;
    background: url('../../assets/threeJs/home/icon_arrow.png') no-repeat center;
    background-size: 100% 100%;
    transform: rotate(180deg);
    margin-left: 8px;
  }
}
.point{
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 1;
  .label{
    position: absolute;
    top: -16px;
    left: -16px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: rgba(255, 255, 255, 1);
    text-align: center;
    line-height: 32px;
    font-weight: 100;
    font-size: 14px;
    cursor: help;
    transform: scale(0,0);
    transition: all 0.3s ease-in-out;
    &::before, &::after{
      content: '';
      background: rgba(255, 255, 255, 1);
      width: 100%;
      height: 100%;
      border-radius: 50%;
      position: absolute;
      left: 50%;
      top: 50%;
      margin-left: -10px;
      margin-top: -10px;
    }
    &::before{
      animation: bounce-wave 1.5s infinite;
    }
    &::after{
      animation: bounce-wave 1.5s -0.4s infinite;
    }
    .label-tips{
      width: 200px;
      height: 88px;
      overflow: hidden;
      position: absolute;
      top: -32px;
      right: -220px;
      font-size: 32px;
      background: rgba(255, 255, 255, .6);
      border: 1px groove rgba(255, 255, 255, .5);
      border-radius: 16px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      box-shadow: 1px 1px 2px rgba(0, 0, 0, .1);
    }
    .cover{
      width: 80px;
      height: 100%;
      .icon{
        display: inline-block;
        height: 100%;
        width: 100%;
        filter: drop-shadow(1px 1px 4px rgba(0, 0, 0, .1));
      }
    }
    .info{
      width: calc(100% - 80px);
      height: 100%;
      overflow: hidden;
      padding-left: 12px;
      p{
        overflow: hidden;
        text-overflow: ellipsis;
        text-align: left;
        text-shadow: 0 1px 1px rgba(0, 0, 0, .1);
      }
      .p1{
        font-size: 24px;
        color: #1D1F24;
        font-weight: 800;
        margin: 12px 0 2px;
      }
      .p2{
        font-size: 18px;
        color: #00aa47;
        font-weight: 500;
      }
    }
    .label-sofa{
      .label-tips{
        left: -220px;
        flex-direction: row-reverse;
        .info{
          padding: 0 12px 0 0;
        }
        p{
          text-align: right;
        }
      }
    }
    .text{
      position: absolute;
      top: 30px;
      left: -120px;
      width: 200px;
      height: 20px;
      border-radius: 4px;
      background: rgba(0, 0, 0, .6);
      border: 1px solid #ffffff;
      color: #ffffff;
      line-height: 1.3em;
      font-weight: 100;
      font-size: 14px;
      opacity: 0;
      transition: opacity 0.3s;
      pointer-events: none;
      text-align: justify;
      text-align-last: left;
    }
  }
  &:hover .text{
    opacity: 1;
  }
  &.visible .label{
    transform: scale(1, 1);
  }
}
@keyframes bounce-wave {
  0% {
    transform: scale(1);
    opacity: 1;
  }
  100% {
    transform: scale(3.6);
    opacity: 0;
  }
}
@keyframes slideInRight {
  from {
    -webkit-transform: translate3d(100%, 0, 0);
    transform: translate3d(100%, 0, 0);
    visibility: visible;
  }
  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}
@keyframes slideInLeft {
  from {
    -webkit-transform: translate3d(-100%, 0, 0);
    transform: translate3d(-100%, 0, 0);
    visibility: visible;
  }
  to {
    -webkit-transform: translate3d(0, 0, 0);
    transform: translate3d(0, 0, 0);
  }
}
</style>

